package com.gitee.hermer.boot.jee.upms.auto.configuration;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import com.gitee.hermer.boot.jee.commons.log.UtilsContext;
import com.gitee.hermer.boot.jee.commons.utils.StringUtils;
import com.gitee.hermer.boot.jee.shiro.cache.Redis2SessionCacheShiro;
import com.gitee.hermer.boot.jee.upms.properties.UpmsShiroCacheProperties;
import com.gitee.hermer.boot.jee.upms.properties.UpmsShiroProperties;
import com.gitee.hermer.boot.jee.upms.shiro.BootShiroRealm;
import com.gitee.hermer.boot.jee.upms.shiro.users.domain.SystemResource;
import com.gitee.hermer.boot.jee.upms.shiro.users.service.impl.SystemResourceServiceImpl;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

@Configuration
@EnableConfigurationProperties(value={UpmsShiroProperties.class,UpmsShiroCacheProperties.class})
@MapperScan("com.gitee.hermer.boot.jee.upms")
@ComponentScan("com.gitee.hermer.boot")
public class UpmsShiroAutoConfiguration extends UtilsContext{
	
	
	@Autowired
	private UpmsShiroProperties properties;
	
	@Autowired
	private UpmsShiroCacheProperties cacheProperties;

	@Bean
	public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}
	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	/**
	 * ShiroFilterFactoryBean 处理拦截资源文件问题。
	 * 注意：单独一个ShiroFilterFactoryBean配置是或报错的，因为在
	 * 初始化ShiroFilterFactoryBean的时候需要注入：SecurityManager
	 *
     Filter Chain定义说明
     1、一个URL可以配置多个Filter，使用逗号分隔
     2、当设置多个过滤器时，全部验证通过，才视为通过
     3、部分过滤器可指定参数，如perms，roles
	 *
	 */
	@Bean
	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager,SystemResourceServiceImpl resource){
		ShiroFilterFactoryBean shiroFilterFactoryBean  = new ShiroFilterFactoryBean();
		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		shiroFilterFactoryBean.setLoginUrl(properties.getLogin());
		shiroFilterFactoryBean.setSuccessUrl(properties.getSuccess());
		shiroFilterFactoryBean.setUnauthorizedUrl(properties.getUnauthorized());
		Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
		
		filterChainDefinitionMap.put(properties.getLogout(), "logout");
		filterChainDefinitionMap.put("/css/**","anon");
		filterChainDefinitionMap.put("/js/**","anon");
		filterChainDefinitionMap.put("/img/**","anon");
		filterChainDefinitionMap.put("/font-awesome/**","anon");
		//自定义加载权限资源关系
		try{
			List<SystemResource> resourcesList = resource.list();
			for(SystemResource resources:resourcesList){

				if (StringUtils.isNotEmpty(resources.getResourceUrl())) {
					String permission = "perms[" + resources.getResourceUrl()+ "]";
					filterChainDefinitionMap.put(resources.getResourceUrl(),permission);
				}
			}
		}catch (Throwable e) {
			error(e.getMessage(),e);
		}
		filterChainDefinitionMap.put("/**", "authc");


		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}
	@Bean
	public SecurityManager securityManager(DefaultWebSessionManager manager){
		DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
		//设置realm.
		securityManager.setRealm(bootShiroRealm());
		// 自定义缓存实现 使用redis
		//securityManager.setCacheManager(cacheManager());
		// 自定义session管理 使用redis
		securityManager.setSessionManager(manager);
		return securityManager;
	}

	@Bean
	public BootShiroRealm bootShiroRealm(){
		BootShiroRealm myShiroRealm = new BootShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * 凭证匹配器
	 * （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
	 *  所以我们需要修改下doGetAuthenticationInfo中的代码;
	 * ）
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher(){
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

		hashedCredentialsMatcher.setHashAlgorithmName(properties.getHash());//散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(2);//散列的次数，比如散列两次，相当于 md5(md5(""));

		return hashedCredentialsMatcher;
	}


	/**
	 *  开启shiro aop注解支持.
	 *  使用代理方式;所以需要开启代码支持;
	 * @param securityManager
	 * @return
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	
	@Bean
	@ConditionalOnProperty(value="com.boot.jee.upms.shiro.cache.enable",havingValue="true",matchIfMissing=false)
	public SessionDAO bootCacheSessionDAO() {
		return new Redis2SessionCacheShiro(cacheProperties.getPrefix(),cacheProperties.getSessionTimeOut());
	}
	

	@Bean
	@ConditionalOnMissingBean(SessionDAO.class)
	public SessionDAO shiroSessionDAO() {
		EnterpriseCacheSessionDAO dao = new EnterpriseCacheSessionDAO();
		dao.setActiveSessionsCacheName(cacheProperties.getPrefix());
		return dao;
	}


	/**
	 * shiro session的管理
	 */
	@Bean
	public DefaultWebSessionManager sessionManager(SessionDAO dao) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setSessionDAO(dao);
		sessionManager.setGlobalSessionTimeout(cacheProperties.getSessionTimeOut());
		return sessionManager;
	}

}
